package org.redmagic.location.web;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.DefaultHttpClient;
import org.redmagic.Request;
import org.redmagic.Response;
import org.redmagic.ResponseDecoder;
import org.redmagic.TransactionManager;

public class IndividualTransactionManager implements TransactionManager {

	private static final Log LOG = LogFactory.getLog(IndividualTransactionManager.class);
	
	private final HttpClient httpClient;
	
//	private Map<String, ResponseDecoder> responseDecoders;
	
	public IndividualTransactionManager(Map<String, ResponseDecoder> responseDecoders) {
//		this.responseDecoders = responseDecoders;
		this.httpClient = new DefaultHttpClient(); // Create a connection pool to speed up the process?
	}
	
	public IndividualTransactionManager() {
	    this.httpClient = new DefaultHttpClient();
	}
	
	@Override
	public Response execute(URI uri, Request request) {
		try {
			return tryExecute(uri, request);
		} catch (Exception e) {
			throw new RuntimeException("Transaction failed", e);
		}
	}
	
	private Response tryExecute(URI uri, Request request) throws ClientProtocolException, IOException {
		Properties resolvedProperties = new Properties();
		
		String baseUri = getBaseUri(uri);
		List<String> queryParams = prepareQueryParams(request.getHierarchy());
		
		for (String placeholder : request.getPlaceholders()) {
			String property = resolvePlaceholder(baseUri, placeholder, queryParams);
			if (property != null) {
				resolvedProperties.put(placeholder, property);
			}
		}

		return new SimpleResponse(resolvedProperties);
	}
	
	private String getBaseUri(URI uri) {
		String baseUri = uri.toString();
		return (baseUri.endsWith("/") ? baseUri : baseUri + "/");
	}
	
	private List<String> prepareQueryParams(List<List<String>> hierarchy) {
		// Does the client-side order of the tags make a difference?
		// It shouldn't if they are sent as a Set in the bulk request
		
		List<String> tags = new ArrayList<String>(hierarchy.size());
		
		for (List<String> hierarchyLevel : hierarchy) {
			tags.add(createTagQueryParamsFromHierarchyLevel(hierarchyLevel));
		}
		
		return tags;
	}
	
	private String createTagQueryParamsFromHierarchyLevel(List<String> tags) {
		StringBuilder builder = new StringBuilder();
		
		for (String tag : tags) {
			builder.append(tag);
			builder.append('&');
		}
		
		return trimLastCharacter(builder.toString());
	}
	
	private String trimLastCharacter(String input) {
		return input.isEmpty() ? "" : input.substring(0, input.length()-1);
	}
	
	private String resolvePlaceholder(String baseUri, String placeholder, List<String> queryParams) {
		String property = null;
		
		for (String queryParam : queryParams) {
			String propertyUri = baseUri + placeholder + "?" + queryParam;
			
			try {
				property = tryResolvePlaceholder(propertyUri);
			} catch (Exception e) {
				e.printStackTrace();
			}
			
			if (property != null) {
				break;
			}
		}
		
		return property;
	}
	
	private String tryResolvePlaceholder(String uri) throws ClientProtocolException, IOException {
		LOG.debug("Trying to retrieve property from : " + uri);
		HttpUriRequest request = prepareRequest(uri);
		HttpResponse httpResponse = httpClient.execute(request);
		String property = handleResponse(httpResponse);
		
		return property;
	}
	
	private HttpUriRequest prepareRequest(String uri) {
		HttpGet get = new HttpGet(uri);
		return get;
	}

	// Does the ResponseDecoder interface need to change, or should we just not use one here?
//	private Properties handleResponse(HttpResponse httpResponse) throws IllegalStateException, IOException {
//		HttpEntity responseEntity = httpResponse.getEntity();
//		ResponseDecoder responseDecoder = getResponseDecoder(responseEntity);
//		return responseDecoder.decode(responseEntity.getContent());
//	}
//	
//	private ResponseDecoder getResponseDecoder(HttpEntity responseEntity) {
//		Header contentType = responseEntity.getContentType();
//		return responseDecoders.get(contentType.getValue());
//	}
	
	// Should this be moved to a response decoder?
	private String handleResponse(HttpResponse httpResponse) throws IOException {
		HttpEntity responseEntity = httpResponse.getEntity();
		BufferedReader reader = new BufferedReader(new InputStreamReader(responseEntity.getContent()));
		return reader.readLine();
	}
	
	public static void main(String[] args) throws URISyntaxException {
		// http://localhost:8080/property/database/host?release=1.0
		
		URI uri = new URI("http://localhost:8080/property/");
		
		List<List<String>> hierarchy = new ArrayList<List<String>>();
		List<String> level1 = new ArrayList<String>();
		level1.add("broken1");
		level1.add("broken2");
		List<String> level2 = new ArrayList<String>();
		level2.add("release=1.0");
		hierarchy.add(level1);
		hierarchy.add(level2);
		
		Set<String> placeholders = new HashSet<String>();
		placeholders.add("database/host");
		placeholders.add("database/password");
		placeholders.add("database/user");
		
		Request request = new SimpleRedMagicRequest(hierarchy, placeholders);
		
		IndividualTransactionManager rtm = new IndividualTransactionManager(null);
		Response response = rtm.execute(uri, request);
		System.out.println(response.getProperties());
	}
}
